﻿using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using Av.Utils;
using System.Text;
using System.Configuration;
using System.Net;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Google.Documents;
//using Google.GData.Documents;
using Google.GData.Spreadsheets;
using LumenWorks.Framework.IO.Csv;
using WebUtils.Models;
using WebUtils.Forms;

namespace WebUtils
{

    /// <summary>
    /// New message for user is available
    /// </summary>
    public delegate void LogMessageHandler(Importance level, string msg, params object[] args);

    /// <summary>
    /// Operation status changed - completed, error, etc...
    /// </summary>
    public delegate void OperationStatusHandler(object sender, OperationStatusEventArgs e);

    public partial class Form1 : Form
    {

        protected GoogleSearchThread m_searchThread = new GoogleSearchThread();

        public static event LogMessageHandler LogMessageEvent;

        protected SymfonyParser m_symParser = new SymfonyParser();
        
        /// <summary>
        /// Systray icon
        /// </summary>
        protected NotifyIcon m_icon = null;



        #region " Get/set text methods (invoked) "

        delegate void SetTextDelegate(string name, string value);
        delegate string GetTextDelegate(string name);

        /// <summary>
        /// Returns text of control.
        /// </summary>
        /// <param name="name">Name of control</param>
        /// <returns>Text of control. Returns "checked"/"" or "" for CheckBox</returns>
        private string GetText(string name)
        {
            string s = "";

            try
            {
                if(this.InvokeRequired)
                {
                    this.Invoke(new GetTextDelegate(GetText), name);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if((ar != null) && (ar.Length > 0))
                    {
                        if(ar[0].GetType() == typeof(CheckBox))
                        {
                            s = ((CheckBox)ar[0]).Checked ? "checked" : "";
                        } else
                        {
                            s = ar[0].Text;
                        }
                    }
                }
            } catch(Exception ex)
            {
            }

            return s;
        }

        /// <summary>
        /// Thread safe AddText
        /// </summary>
        /// <param name="name">Name of control to add text</param>
        /// <param name="value">Text to add</param>
        private void AddText(string name, string value)
        {
            try
            {
                if(this.InvokeRequired)
                {
                    this.Invoke(new SetTextDelegate(AddText), name, value);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if((ar != null) && (ar.Length > 0))
                    {
                        ar[0].Text += value;
                        if(ar[0].GetType() == typeof(TextBox))
                        {
                            ((TextBox)ar[0]).SelectionStart = Int32.MaxValue;
                        }
                    }
                }
            } catch(Exception)
            {
            }
        }

        /// <summary>
        /// Thread safe SetText
        /// </summary>
        /// <param name="name">Name of control to set text</param>
        /// <param name="value">Text to set</param>
        private void SetText(string name, string value)
        {
            try
            {
                if(this.InvokeRequired)
                {
                    this.Invoke(new SetTextDelegate(SetText), name, value);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if((ar != null) && (ar.Length > 0))
                    {
                        ar[0].Text = value;
                    }
                }

            } catch(Exception ex)
            {
            }
        }

        #endregion

        public Form1()
        {
            InitializeComponent();
        }

        private void OnFormClosed(object sender, FormClosedEventArgs e)
        {
            if(m_icon != null)
            {
                m_icon.Dispose();
            }
        }

        private void OnFormLoad(object sender, EventArgs e)
        {
            try
            {
                Log4cs.Log("Starting {0} (v{1})...", Settings.Name, Settings.Version);
                Debug("Starting {0}...", Settings.NameVersion);

                // In case we need only one instance of program
                EnsureSingleApplication();

                this.Text = Settings.NameVersion;

                //Assembly asm = Assembly.GetExecutingAssembly();
                //FileInfo fi = new FileInfo(asm.GetName().Name);
                //using(Stream s = asm.GetManifestResourceStream(string.Format("{0}.av1.ico", fi.Name)))
                //{
                //    Icon = new Icon(s);
                //}

                // Creates icons using emebedded icon
                CreateFormIcons();

                // Create context menu for tray icon
                MenuItem[] arMenu = this.CreateMenuItems();
                if(arMenu != null)
                {
                    m_icon.ContextMenu = new ContextMenu(arMenu);
                }

                // Handles notifications
                m_symParser.NewUserInfo += new LogMessageHandler(Debug);
                m_symParser.NewUserInfo += new LogMessageHandler(Log4cs.Log);

                // Watch operation complete
                m_symParser.OperationStatusChanged += new OperationStatusHandler(SymfonyParserOperationStatusChanged);

                m_searchThread.SearchFinished += new MyNotificationDelegate(OnSearchFinished);
                m_searchThread.PageChanged += new SearchPageChangeDelegate(OnSearchPageChanged);

            } catch(ApplicationException ex)
            {
                Debug(Importance.Error, "Application already run, check logs!");
                MessageBox.Show(ex.ToString(), string.Format("{0} (v{1})", Settings.Name, Settings.Version), MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error loading main form!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                MessageBox.Show("Error loading application!", string.Format("{0} (v{1})", Settings.Name, Settings.Version), MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }
        }

        protected void OnSearchPageChanged(int page)
        {
            Debug("Changin search page to #{0}", page);
            lblSearchPage.Text = "#" + page;
        }

        protected void SymfonyParserOperationStatusChanged(object sender, OperationStatusEventArgs e)
        {
            try
            {
                OperationStatusEventArgs os = (OperationStatusEventArgs)e;

                if( os.Status == OperationStatusEventArgs.OperationStatusType.Failed )
                {
                    Debug(Importance.Error, "Error in {0}!", sender.ToString());
                }

                switch(os.Operation)
                {
                    case OperationStatusEventArgs.OperationType.ProjectParsing:
                        Debug("Project is parsed...");
                        string[] ar = m_symParser.GetApplicationList();
                        RefreshApplicationList(ar);
                        ar = m_symParser.GetModuleList(m_symParser.FirstAppName);
                        RefreshModuleList(ar);
                        break;
                    case OperationStatusEventArgs.OperationType.I18nParsing:
                        break;
                    case OperationStatusEventArgs.OperationType.TemplatesParsing:
                        break;
                    default:
                        break;
                }
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error handling operation changes!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        private void RefreshApplicationList(string[] ar)
        {
            this.lstApplications.Items.Clear();
            this.lstApplications.Items.AddRange(ar);
        }

        private void RefreshModuleList(string[] modules)
        {
            this.lstModuleList.Items.Clear();
            this.lstModuleList.Items.AddRange(modules);
        }

        /// <summary>
        /// Creates tray icon from embedded to project icon. Throws execptions
        /// </summary>
        private void CreateFormIcons()
        {
            // Create tray icon
            Assembly asm = Assembly.GetExecutingAssembly();
            FileInfo fi = new FileInfo(asm.GetName().Name);
            using(Stream s = asm.GetManifestResourceStream(string.Format("{0}.av1.ico", fi.Name)))
            {
                // Create icon to be used in Form and Tray
                Icon icon = new Icon(s);

                Icon = new Icon(icon, icon.Size);

                m_icon = new NotifyIcon();
                m_icon.Visible = true;
                m_icon.Icon = new Icon(icon, icon.Size);

                icon.Dispose();
            }
        }

        /// <summary>
        /// Ensures that application is the only. Throws ApplicationException if there is such application
        /// </summary>
        private void EnsureSingleApplication()
        {
            bool createdNew = false;
            Mutex mx = new Mutex(false, Settings.Name, out createdNew);
            Log4cs.Log(Importance.Debug, "Is mutex created: {0}", createdNew);

            // If application is already running
            if(createdNew == false)
            {
                throw new ApplicationException(Settings.Name + " application is already running!");
            }
        }


        #region " Debug to UI console "

        /// <summary>
        /// Outputs message to UI output console
        /// </summary>
        /// <param name="msg">Message to input, could be used like string.Format()</param>
        /// <param name="args"></param>
        protected void Debug(string msg, params object[] args)
        {
            Debug(Importance.Info, msg, args);
        }

        /// <summary>
        /// Outputs message to UI output console
        /// </summary>
        /// <param name="level">Level of imortance - Error, Info, etc...</param>
        /// <param name="msg">Message to input, could be used like string.Format()</param>
        /// <param name="args"></param>
        protected void Debug(Importance level, string msg, params object[] args)
        {
            StringBuilder sb = new StringBuilder();
            if(level != Importance.No)
            {
                sb.AppendFormat("[{0}] ", level.ToString().ToUpper());
            }

            sb.AppendFormat(msg, args);
            sb.AppendLine();
            this.AddText("txtOutput", sb.ToString());
            txtOutput.SelectionStart = int.MaxValue;
            txtOutput.ScrollToCaret();
        }

        #endregion


        #region " Context menu methods "

        /// <summary>
        /// Creates context menu for tray icon
        /// </summary>
        /// <returns></returns>
        private MenuItem[] CreateMenuItems()
        {
            MenuItem[] arMenu = null;

            try
            {
                // Got quantity of menus
                arMenu = new MenuItem[MyMenu.Size];

                for(int i = 0; i < MyMenu.Size; i++)
                {
                    arMenu[i] = new MenuItem(MyMenu.ToName(i), OnContextMenuClicked);
                }

                // By default status thread is stopped, so disable "Stop" command
                arMenu[MyMenu.Position.Stop].Enabled = false;

                // Format "Version"
                arMenu[MyMenu.Position.Version].Text = string.Format("{0} (v{1})", Settings.Name, Settings.Version);

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error creating menu items!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }

            return arMenu;
        }

        /// <summary>
        /// Handles clicks on systray icon menu
        /// </summary>
        void OnContextMenuClicked(object sender, EventArgs e)
        {
            try
            {
                switch(((MenuItem)sender).Index)
                {
                    case MyMenu.Position.Start:
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Start].Enabled = false;
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Stop].Enabled = true;
                        OnStartStopClicked(null, null);
                        break;
                    case MyMenu.Position.Stop:
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Start].Enabled = true;
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Stop].Enabled = false;
                        OnStartStopClicked(null, null);
                        break;
                    case MyMenu.Position.Version:
                        MessageBox.Show("Simple Aiva Helper by mr. Aleksej Vasinov", Settings.NameVersion, MessageBoxButtons.OK, MessageBoxIcon.Information);
                        break;
                    case MyMenu.Position.Reload:
                        ReloadSettings();
                        break;
                    default:
                        this.Close();
                        break;
                }

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error handling context menu click!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }


        /// <summary>
        /// Reloads setting. Stops thread if necessary
        /// </summary>
        private void ReloadSettings()
        {
            Log4cs.Log("Reloading settings...");
            Settings.Load();
        }

        /// <summary>
        /// Starts/stops application if needed :)
        /// </summary>
        private void OnStartStopClicked(object sender, object e)
        {
            Log4cs.Log("Start/stop is clicked...");
        }

        #endregion

        Thread m_thread = null;

        private void OnSelectSymfonyDirectoryClicked(object sender, EventArgs e)
        {
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            dlg.ShowNewFolderButton = false;
            dlg.RootFolder = Environment.SpecialFolder.MyComputer;
            dlg.SelectedPath = Settings.Path.LastOpenedDirectory;
            if( dlg.ShowDialog() == DialogResult.OK )
            {
                Settings.Path.LastOpenedDirectory = dlg.SelectedPath;
                Log4cs.Log("Selected symfony directory: {0}...", dlg.SelectedPath);
                Debug("Selected symfony directory: {0}...", dlg.SelectedPath);
                if( m_thread == null )
                {
                    Log4cs.Log(Importance.Debug, "Creating SymofnyParser to pass in thread...");
                    m_thread = new Thread(new ParameterizedThreadStart(m_symParser.ParseSymfonyProject));
                }
                Debug("Going to search in symfony directory {0}...", dlg.SelectedPath);
                m_thread.Start(dlg.SelectedPath);
                //Debug("Please wait while thread finish...");
            }
        }

        class TestParser
        {
            public event LogMessageHandler LogMessageAppeared;

            protected void OnLogMessageAppeared(Importance level, string msg, params object[] args)
            {
                if( LogMessageAppeared != null )
                {
                    LogMessageAppeared(level, msg, args);
                }
            }

            public void Start(object obj)
            {
                Random r = new Random();
                int secondsToSleep = r.Next(5) + 1;
                Thread.Sleep(secondsToSleep * 1000);
                LogMessageAppeared(Importance.Debug, "Test this aftet {0} seconds...", secondsToSleep);
            }
        }

        private void OnTestButtonClicked(object sender, EventArgs e)
        {
            //HttpWebRequest rq = (HttpWebRequest)WebRequest.Create("http://localhost/test.php");
            //rq.CookieContainer = new CookieContainer();
            //rq.CookieContainer.Add(new Cookie("test", "xxxx", "/", "localhost"));
            //HttpWebResponse resp = (HttpWebResponse)rq.GetResponse();
            ////Debug(resp.);
            //foreach(var c in resp.Cookies)
            //{
            //    Debug(c.ToString());
            //}

            //TestParser p = new TestParser();
            //p.LogMessageAppeared += new LogMessageHandler(OnTestLogMessageAppeared);
            //Debug("Going to test event...");
            //Thread t = new Thread(new ParameterizedThreadStart(p.Start));
            //t.Start();
            //Debug("Waiting for event...");


            //SymI18nXmlParser p = new SymI18nXmlParser();
            //p.Parse(@"C:\_projects\51state\sf10\apps\51state\modules\booking\i18n\messages.lt.xml");
            //string s = ConfigurationSettings.AppSettings["LastOpenedDirectory"];
            //Debug("Last opened directory: {0}", s);
            //Settings.Path.LastOpenedDirectory = "xxx";

            //foreach(string key in ConfigurationManager.)
            //{
            //    Debug(key);
            //}
            //SettingElementCollection c = new SettingElementCollection();
            //s = c.Get("LastOpenedDirectory").Value.ToString();
            //Debug("Last opened directory: {0}", s);
            //SymfonyParser p = new SymfonyParser();
            //string s = p.GetEnquotedString(" __('Mirrors') ?>:</", 4);
            //Log4cs.Log("Got: [{0}]", s);
            //Debug("[{0}]", s);

            //s = p.GetEnquotedString(" __(\"Mirrors' is \\\"!\") ?>:</", 4);
            //Log4cs.Log("Got: [{0}]", s);
            //Debug("[{0}]", s);
        }

        void OnTestLogMessageAppeared(Importance level, string msg, params object[] args)
        {
            Debug(level, msg, args);
        }

        private void OnI18nSaveClicked(object sender, EventArgs e)
        {
            m_symParser.SaveAsXml("D:\\Temp\\51state.csv");
        }

        

        private void OnGetI18nFileClicked(object sender, EventArgs e)
        {
            try
            {
                WebClient wc = new WebClient();
                wc.DownloadDataCompleted += new DownloadDataCompletedEventHandler(OnI18nFileDownloadCompleted);
                string culture = lstCulture.SelectedItem.ToString();
                string moduleName = lstModuleList.SelectedItem.ToString();
                Log4cs.Log(Importance.Debug, "Going to download for module {0} (culture: {1})", moduleName, culture);
                if(!string.IsNullOrEmpty(culture) && !string.IsNullOrEmpty(moduleName))
                {
                    Debug("Downloading I18N file from server...");
                    string uriToDownload = string.Format("http://www.interateitis.lt/blogv2.php/{0}/system/seei18n/moduletr/{1}", culture, moduleName);
                    Uri uri = new Uri(uriToDownload);

                    wc.DownloadDataAsync(uri);
                } else
                {
                    Debug(Importance.Error, "Check culture and module name!");
                }
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error getting I18N file from server!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        void OnI18nFileDownloadCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            try
            {
                Log4cs.Log("Got {0} bytes from server...", e.Result.Length);
                string filename = "D:\\temp\\51\\msg.xml";
                Debug("Saving I18N file as {0}...", filename);
                string s = e.Result == null ? "" : Encoding.UTF8.GetString(e.Result);
                File.WriteAllText(filename, s);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error saving downloaded I18N file!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        private void OnPostI18nFileClicked(object sender, EventArgs e)
        {
            try
            {
                string filename = "D:\\temp\\51\\msg.xml";
                Log4cs.Log("POSTing {0} to server...", filename);

                WebClient wc = new WebClient();
                wc.UploadFileCompleted += new UploadFileCompletedEventHandler(OnI18nFileUploadCompleted);

                Uri uri = new Uri("http://www.interateitis.lt/blogv2.php/lt/system/translation");
                wc.UploadFileAsync(uri, "POST", filename);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error POSTing I18N file to server!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        void OnI18nFileUploadCompleted(object sender, UploadFileCompletedEventArgs e)
        {
            Log4cs.Log("File is sent to server: {0}", Encoding.UTF8.GetString(e.Result));
        }

        string m_tempFile = "d:\\temp\\~googlesearch.htm";
        private void OnSearchButtonClicked(object sender, EventArgs e)
        {
            try
            {
                Log4cs.Log("Keyword: {0}", txtKwd.Text);
                string domain = txtDomainToLookFor.Text.Trim();
                string kwd = txtKwd.Text.Trim();
                Debug("Searching for \"{0}\" domain [{1}]", kwd, domain);
                //string query = string.Format("http://www.google.com/search?pws=0&rlz={0}&ie=UTF-8&q={1}", rlz, kwd);
                m_searchThread.Get(domain, kwd);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error searching by keyword!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error searching!");
            }
        }

        void OnSearchFinished(object sender, SearchEventArgs args)
        {
            if(args.IsOk)
            {
                Debug("Search completed successfully!");
                if(args.Position != 0)
                {
                    Debug("Found at {0} page", args.Position / 10 + 1);
                } else
                {
                    Debug("Not found in 100 pages!");
                }
                //File.WriteAllText(m_tempFile, args.Result);
            } else
            {
                Debug(Importance.Error, "Error searching!");
            }
        }

        protected string m_i18nCsvFilename = "";
        private void OnI18nCsvFileClicked(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "CSV files|*.csv;*.tsv|All files|*.*";
            if(dlg.ShowDialog() == DialogResult.OK)
            {
                m_i18nCsvFilename = dlg.FileName;
            }
            txtI18nCsvFilename.Text = m_i18nCsvFilename;
        }

        private void OnParseI18nCsvButtonClicked(object sender, EventArgs e)
        {
            ParseI18nCsvFile(txtI18nCsvFilename.Text);
        }

        protected Dictionary<int, string> m_errors = null;

        /// <summary>
        /// Saves translated text (from google docs) to symfony I18N messages files (module\i18n\messages.YY.xml)
        /// </summary>
        /// <param name="filename"></param>
        private void ParseI18nCsvFile(string filename)
        {
            try
            {
                Debug("Parsing {0}...", filename);
                string[] csv = File.ReadAllLines(filename);
                Log4cs.Log(Importance.Debug, "There are {0} lines in {1}...", csv.Length, filename);
                m_errors = new Dictionary<int, string>();

                StringBuilder sbLt = new StringBuilder();
                StringBuilder sbRu = new StringBuilder();
                StringBuilder sbEn = new StringBuilder();
                string moduleName = string.Empty;

                int transId = 1;
                for(int i = 0; i < csv.Length; i++)
                {
                    try
                    {
                        string[] ar = csv[i].Split("\t".ToCharArray());
                        if(ar.Length > 3)
                        {

                            // Got new module
                            if(!string.IsNullOrEmpty(ar[0].Trim()))
                            {
                                // We have to save
                                if(moduleName != string.Empty)
                                {
                                    sbLt.AppendLine("  </body>\n</file>\n</xliff>");
                                    sbRu.AppendLine("  </body>\n</file>\n</xliff>");
                                    sbEn.AppendLine("  </body>\n</file>\n</xliff>");
                                    string dirToSave = string.Format("D:\\Temp\\51\\translate\\{0}\\i18n", moduleName);
                                    Debug("Going to save in: {0}", dirToSave);
                                    if( !Directory.Exists(dirToSave) )
                                    {
                                        Log4cs.Log("Creating directory {0}...", dirToSave);
                                        Debug("Creating directory {0}...", dirToSave);
                                        Directory.CreateDirectory(dirToSave);
                                    }
                                    
                                    string fileToSave = string.Format("{0}\\messages.lt.xml", dirToSave);
                                    Log4cs.Log("Saving I18N file: {0}", fileToSave);
                                    File.WriteAllText(fileToSave, sbLt.ToString());
                                    
                                    fileToSave = string.Format("{0}\\messages.ru.xml", dirToSave);
                                    //fileToSave = string.Format("D:\\Temp\\51\\{0}.ru.xml", moduleName);
                                    Log4cs.Log("Saving I18N file: {0}", fileToSave);
                                    File.WriteAllText(fileToSave, sbRu.ToString());
                                    
                                    fileToSave = string.Format("{0}\\messages.en_US.xml", dirToSave);
                                    Log4cs.Log("Saving I18N file: {0}", fileToSave);
                                    File.WriteAllText(fileToSave, sbEn.ToString());

                                    sbRu.Remove(0, sbRu.Length);
                                    sbLt.Remove(0, sbLt.Length);
                                    sbEn.Remove(0, sbEn.Length);
                                }

                                transId = 1;
                                moduleName = ar[0].Trim();
                                Log4cs.Log("New I18N module is: {0}", moduleName);
                                sbLt.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE xliff PUBLIC \"-//XLIFF//DTD XLIFF//EN\" \"http://www.oasis-open.org/committees/xliff/documents/xliff.dtd\"><xliff version=\"1.0\">  <file source-language=\"EN\" target-language=\"lt\" datatype=\"plaintext\" original=\"messages\" product-name=\"messages\">  <header/>  <body>");
                                sbRu.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE xliff PUBLIC \"-//XLIFF//DTD XLIFF//EN\" \"http://www.oasis-open.org/committees/xliff/documents/xliff.dtd\"><xliff version=\"1.0\">  <file source-language=\"EN\" target-language=\"ru\" datatype=\"plaintext\" original=\"messages\" product-name=\"messages\">  <header/>  <body>");
                                sbEn.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE xliff PUBLIC \"-//XLIFF//DTD XLIFF//EN\" \"http://www.oasis-open.org/committees/xliff/documents/xliff.dtd\"><xliff version=\"1.0\">  <file source-language=\"EN\" target-language=\"en\" datatype=\"plaintext\" original=\"messages\" product-name=\"messages\">  <header/>  <body>");
                            }

                            sbLt.AppendFormat("  <trans-unit id=\"{0}\">\n    <source>{1}</source>\n    <target>{2}</target>\n  </trans-unit>", transId, ar[1], ar[2]).AppendLine();
                            sbRu.AppendFormat("  <trans-unit id=\"{0}\">\n    <source>{1}</source>\n    <target>{2}</target>\n  </trans-unit>", transId, ar[1], ar[3]).AppendLine();
                            sbEn.AppendFormat("  <trans-unit id=\"{0}\">\n    <source>{1}</source>\n    <target>{2}</target>\n  </trans-unit>", transId, ar[1], ar[4]).AppendLine();
                            transId++;
                        } else
                        {
                            m_errors.Add((i + 1), "Less than 3 params!");
                        }
                    } catch(Exception ex)
                    {
                        Log4cs.Log(Importance.Error, "Error parsing line #{0}!", i);
                        Log4cs.Log(Importance.Debug, ex.ToString());
                        m_errors.Add((i+1), "Error on line #" + i);
                    }
                }

                //Debug("LT is set to clipboard..");
                //Clipboard.SetText(sbLt.ToString() + Environment.NewLine + Environment.NewLine + sbRu.ToString());

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error parsing CSV file!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error parsing I18N CSV file!");
            }
        }

        private void OnChooseCommonFileClicked(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "CSV files|*.csv;*.tsv|All files|*.*";
            if(dlg.ShowDialog() == DialogResult.OK)
            {
                txtCsvPricesFile.Text = dlg.FileName;
            }
        }

        protected Dictionary<string, Product> _products = new Dictionary<string, Product>();
        private void OnVejuvaCsvFileClicked(object sender, EventArgs e)
        {
            string filename = this.GetText("txtCsvPricesFile");
            int parsed = 0;
            string code = "";
            Product p = null;
            try
            {
                Debug("Parsing CSV file {0}", filename);
                _products = new Dictionary<string, Product>();
                Dictionary<string, string> duplicates = new Dictionary<string, string>();
                using( CsvReader reader = new CsvReader(new StreamReader(filename), false) )
                {
                    while(reader.ReadNextRecord())
                    {
                        code = reader[Product.Fields.Code].Trim();
                        if( !_products.ContainsKey(code) )
                        {
                            p = new Product {
                                Code = code
                                , Name = reader[Product.Fields.Name].Trim()
                                , PriceText = reader[Product.Fields.Price].Trim()
                            };
                            _products.Add(code, p);
                            if( p.Price == 0 )
                            {
                                duplicates[code] = p.PriceText;
                            }
                        }
                        parsed++;
                    }
                    Debug("Done parsing, got {0} products and {1} empty (0.00) prices...", _products.Count, duplicates.Count);
                }  // END USING ( CSV reader )

                StringBuilder sb = new StringBuilder();
                foreach(var kvp in _products)
                {
                    //sb.AppendFormat("{0}: {1}", kvp.Key, kvp.Value).AppendLine();
                    sb.AppendFormat("{0}\t{1}", kvp.Value.Code, kvp.Value.PriceText).AppendLine();
                }
                Debug("Set prices to clipboard");
                Clipboard.SetText(sb.ToString());
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error parsing vejuva.lt prices CSV file {0}!", filename);
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error parsing vejuva.lt CSV file {0}!", filename);
            }
        }

        private void SearchForProductCode(string code)
        {
            Debug("Searching for code: {0}", code);
            if( _products.ContainsKey(code) )
            {
                Debug("{0}{1}, {2}", code.PadRight(16, ' '), _products[code].PriceText, _products[code].Name);
            }else
            {
                Debug("No such code!");
            }
        }

        private void OnProductCodeKeyPressed(object sender, KeyPressEventArgs e)
        {
            if(e.KeyChar == 13)
            {
                SearchForProductCode(this.GetText("txtProductCode").Trim());
            }
            if((e.KeyChar < '0') || (e.KeyChar > '9'))
            {
                Console.Beep();
                e.Handled = true;
            }
        }

        private void OnGenerateButtonClicked(object sender, EventArgs e)
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                string[] lines = txtGetterSetters.Lines;
                Regex rx = new Regex(@"(protected|private)([^$]+)(\$)([^;]+)");
                for(int i = 0; i < lines.Length; i++)
                {
                    string line = lines[i].Trim();
                    Match m = rx.Match(line);
                    if( m.Success )
                    {
                        string varName = m.Groups[4].Value;
                        string[] ar = varName.Split("_".ToCharArray());
                        string methodName = "";
                        for(int j = 0; j < ar.Length; j++)
                        {
                            methodName += string.Format("{0}{1}", ar[j].Substring(0, 1).ToUpper(), ar[j].Substring(1));
                        }
                        sb.AppendFormat("public function get{1}(){0}{{{0}    return $this->{2};{0}}}", Environment.NewLine, methodName, varName).AppendLine();
                        sb.AppendFormat("public function set{1}($value){0}{{{0}    $this->{2} = $value;{0}}}", Environment.NewLine, methodName, varName).AppendLine();
                    }
                }
                Debug("Getter/setter:{0}{1}", Environment.NewLine, sb.ToString());
                Clipboard.SetText(sb.ToString());
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error generating getters/setters!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                
            }
        }

        private enum AutoMakersParsingState { Makers, Models };
        protected Dictionary<string, string> m_arMakers = new Dictionary<string, string>();

        private void OnAutoMakersButtonClicked(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            string makerName, makerId;
            AutoMakersParsingState state = AutoMakersParsingState.Makers;
            // Scan maker in such format: "13"=maker name"
            Regex rx = new Regex(@"("")(\d+)([^\w]+)([^""]+)");
            Regex rxMakerMain = new Regex(@"^("")(\d+)("")( = \{)([^}]+)");
            Regex rxMakerModels = new Regex(@"("")(\d+)(\""\ \: \"")([^""]+)");
            Match m = null;
            string filenam = "";
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "All file (*.*)|*.*|Text files (*.txt)|*.txt";
            if( dlg.ShowDialog() == DialogResult.OK )
            {
                sb.AppendLine("ID\tMaker\tModel");
                filenam = dlg.FileName;
                Debug("Parsing {0}...", filenam);
                string[] lines = File.ReadAllLines(filenam);
                for(int i = 0; i < lines.Length; i++)
                {
                    switch(state)
                    {
                        case AutoMakersParsingState.Makers:
                            // Models are separated by new lines (in text files)
                            if(lines[i].Trim().Length < 1)
                            {
                                Debug("Models ended on line #{0}", i);
                                state = AutoMakersParsingState.Models;
                            } else
                            {
                                m = rx.Match(lines[i]);
                                if(m.Success)
                                {
                                    //Debug("Got maker: {0} (ID: {1})", m.Groups[4].Value, m.Groups[2].Value);
                                    m_arMakers.Add(m.Groups[2].Value, m.Groups[4].Value);
                                }
                            }
                            break;
                        case AutoMakersParsingState.Models:
                            if( lines[i].Trim().Length > 1 )
                            {
                                // Find ID of maker and list of models
                                m = rxMakerMain.Match(lines[i]);
                                makerId = m.Groups[2].Value;
                                makerName = m_arMakers.ContainsKey(makerId) ? m_arMakers[makerId] : "N/A";
                                // If parsed and such maker exists
                                if( m.Success )
                                {
                                    sb.AppendFormat("{0}\t{1}\tX", makerId, makerName).AppendLine();
                                    // Let's parse all models for found maker
                                    Match mm = rxMakerModels.Match(m.Groups[5].Value);
                                    while(mm.Success)
                                    {
                                        // If it is real model, not "--kita--"
                                        if( mm.Groups[2].Value != "999" )
                                        {
                                            sb.AppendFormat("0\t\t{0}", mm.Groups[4].Value).AppendLine();
                                        }
                                        //Debug("    {0}", mm.Groups[4].Value);
                                        mm = mm.NextMatch();
                                    }
                                }
                            }
                            break;
                    }
                }  // END FOR ( all lines in file )
                Clipboard.SetText(sb.ToString());
            }
        }

        PasswordForm.Credentials m_userCredentials = new PasswordForm.Credentials();
        private void OnMakersToSqlButtonClicked(object sender, EventArgs e)
        {
            try
            {
                if( string.IsNullOrEmpty(m_userCredentials.Password) )
                {
                    PasswordForm f = new PasswordForm();
                    m_userCredentials = f.GetCredentials("proglamer@gmail.com");
                    //Debug("Authenticating as {0}/{1}", );
                }
                SpreadsheetsService spreadService = new SpreadsheetsService(Settings.GoogleApp.ApplicationName);
                spreadService.setUserCredentials(m_userCredentials.Username, m_userCredentials.Password);
                SpreadsheetQuery query = new SpreadsheetQuery();
                query.Title = "Auto makers";
                query.Exact = true;

                SpreadsheetFeed feed = spreadService.Query(query);
                Log4cs.Log("Got {0} spreadsheets for 'Auto makers'", feed.Entries.Count);
                SpreadsheetEntry entry = (SpreadsheetEntry)feed.Entries[0];
                //Log4cs.Log("There are {0} worksheets in 'Auto makers'...", entry.Worksheets.Entries.Count);
                WorksheetEntry worksheet = (WorksheetEntry)entry.Worksheets.Entries[0];
                Log4cs.Log("Worksheet has {0}x{1}", worksheet.ColCount.Count, worksheet.RowCount.Count);
/*
                DocumentsService myService = new DocumentsService("Test app for google");
                myService.setUserCredentials(m_userCredentials.Username, m_userCredentials.Password);

                Debug("Getting 'Auto makers' spreadsheet...");
                DocumentsListQuery query = new DocumentsListQuery();
                query.Title = "Auto makers";
                query.TitleExact = true;
                DocumentsFeed feed = myService.Query(query);
                foreach(var doc in feed.Entries)
                {
                    Debug("> " + doc.Title.Text);
                }
*/
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error getting list of documents!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error getting document!");
            }
        }

        private void OnMakersFileToSqlButtonClicked(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog dlg = new OpenFileDialog();
                StringBuilder sb = new StringBuilder();
                string[] lines;
                string[] ar;
                int producerId = 0;
                string makerId, maker, model;
                // To save in DB w/o collision
                int incrementId;

                string makerSqlFmt = "INSERT INTO `list_values_tree` (`id`, `lng`, `list_id`, `parent_id`, `status_id`, `value`) VALUES({0}, 1, 1001, 0, 1, '{1}');";
                string modelSqlFmt = "INSERT INTO `list_values_tree` (`lng`, `list_id`, `parent_id`, `status_id`, `value`) VALUES(1, 1001, {0}, 1, '{1}');";

                dlg.Filter = "Text files|*.tsv|All files|*.*";
                if(dlg.ShowDialog() == DialogResult.OK)
                {
                    incrementId = int.TryParse(txtIdToIncrement.Text.Trim(), out incrementId) ? incrementId : 10000;
                    lines = File.ReadAllLines(dlg.FileName);
                    // Do NOT parse header line
                    for(int i = 1; i < lines.Length; i++)
                    {
                        Log4cs.Log("Parsing: {0}", lines[i]);
                        ar = lines[i].Split("\t".ToCharArray());
                        if(ar.Length > 2)
                        {
                            makerId = ar[0].Trim();
                            maker = ar[1].Trim();
                            model = ar[2].Trim();


                            // Got new auto maker ID
                            if(makerId != "0")
                            {
                                producerId = int.Parse(makerId);
                                // Make it safe to insert
                                producerId += incrementId;

                                sb.AppendFormat("-- {0} ----------", maker).AppendLine();
                                sb.AppendFormat(makerSqlFmt, producerId, maker).AppendLine();
                            } else
                            {
                                // Model, which belongs to producer
                                sb.AppendFormat(modelSqlFmt, producerId, model).AppendLine();
                            }

                            

                        } else
                        {
                            Debug("Line #{0} contains less than 3 elements!", i+1);
                        }
                    }  // END FOR (lines in file)

                    Debug("Makers file is parsed (copied to Clipboard)");
                    Clipboard.SetText(sb.ToString());
                }
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error parsing auto makers!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error parsing auto makers!");
            }
        }

        private void OnMakersModelsFileToSqlButtonClicked(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog dlg = new OpenFileDialog();
                dlg.Filter = "CSV files|*.csv|All files|*.*";
                int columns = -1;
                bool firstLine = true;
                Dictionary<int, Maker> makersModels = new Dictionary<int, Maker>();
                if(dlg.ShowDialog() == DialogResult.OK)
                {
                    using(CsvReader reader = new CsvReader(new StreamReader(dlg.FileName), false))
                    {
                        while(reader.ReadNextRecord())
                        {
                            if( firstLine )
                            {
                                columns = reader.FieldCount;
                                Log4cs.Log("There are {0} columns in CSV file", columns);
                            }

                            for(int i = 0; i < columns; i++)
                            {
                                // 1st, 3rd, 5th columns in first line contains makers
                                if(firstLine && (i % 2 == 0))
                                {
                                    Log4cs.Log("Got maker: {0}", reader[i]);
                                    makersModels.Add(i + 1, new Maker { Name = reader[i] });
                                }
                                else if(i % 2 == 1)
                                {
                                    // Model
                                    string model = reader[i].Trim();
                                    if(!string.IsNullOrEmpty(model))
                                    {
                                        makersModels[i].Models.Add(model);
                                    }
                                }
                            }

                            firstLine = false;
                        }
                    }

                    int currentId = int.Parse(txtIdToIncrement.Text);
                    int parentId = currentId;
                    string makerSqlFmt = "INSERT INTO `list_values_tree` (`id`, `lng`, `list_id`, `parent_id`, `status_id`, `value`) VALUES({0}, 1, 1007, 0, 1, '{1}');";
                    string modelSqlFmt = "INSERT INTO `list_values_tree` (`lng`, `list_id`, `parent_id`, `status_id`, `value`) VALUES(1, 1007, {0}, 1, '{1}');";
                    StringBuilder sb = new StringBuilder();

                    Log4cs.Log("Got {0} makers", makersModels.Count);
                    foreach(var maker in makersModels)
                    {
                        Log4cs.Log("List of {0} models:", maker.Value.Name);
                        sb.AppendFormat("/* ----- {0} -- */", maker.Value.Name).AppendLine();
                        sb.AppendFormat(makerSqlFmt, parentId, maker.Value.Name).AppendLine();

                        currentId = parentId + 1;
                        foreach(var model in maker.Value.Models)
                        {
                            Log4cs.Log("  {0}", model);
                            sb.AppendFormat(modelSqlFmt, parentId, model.Replace("'", "&apos;")).AppendLine();
                            currentId++;
                        }

                        parentId = currentId;
                    }

                    if(MessageBox.Show("Copy SQL to Clipboard?!", Settings.NameVersion, MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        Clipboard.SetText(sb.ToString());
                    }
                }
                // List all rows. 1st is maker, 2nd is models, 3rd is maker, 4th is models and so on...
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error getting list of documents!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error getting document!");
            }

        }

        private void OnGenerateSchemaButtonClicked(object sender, EventArgs e)
        {
            SchemaForm f = new SchemaForm();
            f.StartPosition = FormStartPosition.CenterScreen;
            f.ShowDialog();
        }

        private void OnOpenLvtFormButtonClicked(object sender, EventArgs e)
        {
            LvtForm f = new LvtForm();
            f.StartPosition = FormStartPosition.CenterScreen;
            f.ShowDialog();
        }

    }  // END CLASS Form1

}
